#include "Type.h"
#include <memory>
#include <cassert>

std::shared_ptr<BuildInType> Type::CharType = 
            std::make_shared<BuildInType>(BuildInType::Kind::Char, 1, 1);
std::shared_ptr<BuildInType> Type::ShortType = 
            std::make_shared<BuildInType>(BuildInType::Kind::Short, 2, 2);
std::shared_ptr<BuildInType> Type::IntType = 
            std::make_shared<BuildInType>(BuildInType::Kind::Int, 4, 4);
std::shared_ptr<BuildInType> Type::LongType = 
            std::make_shared<BuildInType>(BuildInType::Kind::Long, 8, 8);

bool Type::IsIntegerType() const {
    if (TC == TypeClass::BuiltInType){
        auto bi = dynamic_cast<const BuildInType*>(this);
        return bi->GetKind() == BuildInType::Kind::Char
            || bi->GetKind() == BuildInType::Kind::Short
            || bi->GetKind() == BuildInType::Kind::Int
            || bi->GetKind() == BuildInType::Kind::Long;
    }
    return false;
}

bool Type::IsPointerType() const {
    return TC == Type::TypeClass::PointerType;
}

bool Type::IsFunctionType() const {
    return TC == Type::TypeClass::FunctionType;
}

bool Type::IsArrayType() const {
    return TC == Type::TypeClass::ArrayType;
}

void TypeVisitor::VisitProgramNode(ProgramAstNode *node) {
    for (auto &func : node->Funcs) {
        func->Accept(this);
    }
}

void TypeVisitor::VisitFunctionNode(FunctionNode *node) {
    for (auto &statm : node->Statms) {
        statm->Accept(this);
    }
}

void TypeVisitor::VisitExprStatmNode(ExprStatmNode *node) {
    if (node->Lhs) {
        node->Lhs->Accept(this);
        node->Ty = node->Lhs->Ty;
    }
}

void TypeVisitor::VisitFuncCallNode(FuncCallNode *node) {
    for (auto &arg : node->Args) {
        arg->Accept(this);
    }
    node->Ty = Type::LongType;
}

void TypeVisitor::VisitDeclarationStatmNode(DeclarationStatmNode *node) {
    for (auto &n : node->AssignNodes) {
        n->Accept(this);
    }
}

void TypeVisitor::VisitIfStatmNode(IfStatmNode *node) {
    node->Cond->Accept(this);
    node->Then->Accept(this);
    if (node->Else) {
        node->Else->Accept(this);
    }
}

void TypeVisitor::VisitWhileStatmNode(WhileStatmNode *node) {
    node->Cond->Accept(this);
    node->Then->Accept(this);
}

void TypeVisitor::VisitForStatmNode(ForStatmNode *node) {
    if (node->Init) {
        node->Init->Accept(this);
    }

    if (node->Cond) {
        node->Cond->Accept(this); 
    }
    node->Then->Accept(this);
    if (node->Inc) {
        node->Inc->Accept(this);        // 5. 条件变化
    }
    
}

void TypeVisitor::VisitBlockStatmNode(BlockStatmNode *node) {
    for (auto &statm : node->Statms) {
        statm->Accept(this);
    }
}

void TypeVisitor::VisitReturnStatmNode(ReturnStatmNode *node) {
    node->Lhs->Accept(this);
    node->Ty = node->Lhs->Ty;
}

void TypeVisitor::VisitAssignExprNode(AssignExprNode *node) {
    node->Lhs->Accept(this);
    node->Rhs->Accept(this);
    node->Ty = node->Lhs->Ty;
}

void TypeVisitor::VisitBinaryNode(BinaryAstNode *node) {
    node->Lhs->Accept(this);
    node->Rhs->Accept(this);
    switch (node->BinOp) {
        case BinaryOperator::Add :
        case BinaryOperator::Sub :
        case BinaryOperator::Mul :
        case BinaryOperator::Div :
        case BinaryOperator::PtrAdd :
        case BinaryOperator::PtrSub :
            node->Ty = node->Lhs->Ty;
            break;
        case BinaryOperator::Equal :
        case BinaryOperator::PipeEqual :
        case BinaryOperator::Greater :
        case BinaryOperator::GreaterEqual :
        case BinaryOperator::Lesser :
        case BinaryOperator::LesserEqual :
        case BinaryOperator::PtrDiff :
            node->Ty = Type::LongType;
            break;
        default:
            assert(0);
            break;
    }
}

void TypeVisitor::VisitUnaryNode(UnaryAstNode *node) {
    node->Lhs->Accept(this);
    switch (node->UnaryOp) {
        case UnaryOperator::Plus :
        case UnaryOperator::Minus : {
            node->Ty = node->Lhs->Ty;
            break;
        }
        case UnaryOperator::Deref : {
            if (node->Lhs->Ty->IsPointerType()) {
                node->Ty = std::dynamic_pointer_cast<PointerType>(node->Lhs->Ty)->Base;
            } else if (node->Lhs->Ty->IsArrayType()) {
                node->Ty = std::dynamic_pointer_cast<ArrayType>(node->Lhs->Ty)->elementType;
            } else {
                printf("invalid deref operator!\n");
                assert(0);
            }
            break;
        }
        case UnaryOperator::Amp : {
            node->Ty = std::make_shared<PointerType>(node->Lhs->Ty);
            break;
        }
        default:
            break;
    }
}

void TypeVisitor::VisitConstantNode(ConstantAstNode *node) {
    node->Ty = Type::LongType;
}

void TypeVisitor::VisitSizeofNode(SizeofNode *node) {
    node->Lhs->Accept(this);
    node->Ty = node->Lhs->Ty;
}

void TypeVisitor::VisitVarNode(VarAstNode *node) {
    node->Ty = node->VarObj->Ty;
}
